python basics

Lorenzo Drufuca

12/10/2023

get started

Source Code and Machine Code

We generally write a computer program using a high-level language. A high-level language is one that is understandable by us, humans. This is called source code.

However, a computer does not understand high-level language. It only understands the program written in 0’s and 1’s in binary, called the machine code.

Compiler and Interpreter

To convert source code into machine code, we use either a compiler or an interpreter.

Both compilers and interpreters are used to convert a program written in a high-level language into machine code understood by computers. However, there are differences between how an interpreter and a compiler works.

Interpreter Compiler
Translates program one statement at a time Scans the entire program and translates it as a whole into machine code
No intermediate object code is generated, hence is memory efficient Generates intermediate object code which further requires linking, hence requires more memory
Programming languages like JavaScript, Python, Ruby use interpreters Programming languages like C, C++, Java use compilers

Interpreter: Immediate Mode

Once Python is installed, typing python in the command line will invoke the interpreter in immediate mode. You can directly type in Python code, and press Enter to get the output.

1 + 1
2

Then you could use

  • quit()

for exit

quit()

Interpreter: Text Editing Software

It is possible to use any text editing software to write a Python script file. You just need to save it with the .py extension.

  • open a text editor of you choice
  • write print(“Hello world!”)
  • save the file as script_1.py
  • close the text editor
print("Hello world!")

Now you can lanch your script as a program inside BASH by invoking the corresponding interpreter.

python script_1.py

Intepreter: IDE

IDE (Integrated Development Environment) is a piece of software (basically a text editor) that provides useful features like code hinting, syntax highlighting and checking, file explorers, etc. to the programmer for application development.

Interpreter Jupyter Lab

JupyterLab is a web-based interactive development environment for Jupyter Notebooks, code, and data.

The Jupyter Notebook is an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and narrative text.

We will use Juyter Notebook as our workspace for Python coding.

Identifiers and Keywords

Identifiers

An identifier is a name given to entities like class, functions, variables, etc. It helps to differentiate one entity from another.

  • Identifiers can be a combination of letters in lowercase (a to z) or uppercase (A to Z) or digits (0 to 9) or an underscore (**_**)
  • An identifier cannot start with a digit
  • An identifier cannot start with special symbols like (! @ $ and so on…)

Naming convention for identifier

snake_case_name
camelCaseName
CapWordsName
MACRO_CASE_NAME

Keywords

Keywords are the reserved words in Python. It is not possible to use a keywords as an identifier.

There are 33 keywords in Python 3.7. This number can vary slightly over the course of time.

Statement, Indentation and Comments

Statements

Instructions that a Python interpreter can execute are called statements. The end of a statement is marked by a newline character even if is possible to make multi-line statements.

# Single line statements
a = 1
b = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Multi line statement
a = 1 + 2 + 3 + \
    4 + 5 + 6 + \
    7 + 8 + 9
colors = ['red',
          'blue',
          'green']

Commments

They describe what is going on inside a program, so that a person looking at the source code does not have a hard time figuring it out. You might forget the key details of the program you just wrote in a month’s time. So taking the time to explain these concepts in the form of comments is always useful.

In Python, is used the hash (#) symbol to start writing a comment. Everything after the hash symbol will not be executed by the interpreter.

# This is a comment
# The following code print out Hello
print('Hello')

""" This is an 
example of a multi
line comment """

Indentation

A code block (body of a function, loop, etc.) starts with indentation and ends with the first unindented line. The amount of indentation is up to you, but it must be consistent throughout that block.

Generally, four whitespaces are used for indentation or alternatively tabs.

for i in range(1,11):
    print(i)
    if i == 5:
        break

Variables

Variables

A variable is a named location used to store data in the memory. It is helpful to think of variables as a container that holds data that can be changed later in the program.

You can use the assignment operator = to assign a value to a variable.

number = 117
website = "www.bungie.net"

If you write in a chunk a variable and run it you will visualize it

number

or

website

What happen if you write more than one variable in a chunk and run it?

number
website

Python will always visualize the output of the last statement of the chunk, in this example the last variable.

To check the value of multiple variables together you could use the function

  • print()
print(number)
print(website)

Changing the value of a variable.

Visualize the variable

website

or

print(website)

Assign a new value to the variable

website = "www.343industries.com"

and visualize the variable again

website

or

print(website)
ta

----

Read the following code and try to answer with out running it.
Which is the value of a and b?

```python
a = 1
b = 2
b = a + b

And find out the solution:

print(a)
print(b)

Remember a variable will not change without assigning it to a new value.

Try to not run the code, which is the value of a, b and c?

a = 5
b = a
c = b + 3

And find out the solution:

print(a)
print(b)
print(c)

Data Types

Data Types

Every value in Python has a datatype. Since everything is an object in Python programming, data types are actually classes and variables are instance (object) of these classes (We will see this later).

Numbers

Integers, floating point numbers and complex numbers fall under Python numbers category. They are defined as int, float and complex classes in Python.

It is possible to use function

  • type()

to know which class a variable or a value belongs to.

a = 5
type(a)
a = 2.0
type(a)
a = 1+2j
type(a)

Integers can be of any length, it is only limited by the memory available.

A floating-point number is accurate up to 15 decimal places. Integer and floating points are separated by decimal points. 1 is an integer, 1.0 is a floating-point number.

Complex numbers are written in the form, x + yj, where x is the real part and y is the imaginary part.

List

List is an ordered sequence of items. It is one of the most used datatype in Python and is very flexible. All the items in a list do not need to be of the same type.

Declaring a list is pretty straight forward. Items separated by commas are enclosed within brackets [ ].

a = [1, 2.2, 'python']

Which type are the elements of the list above?

print(type(1))
print(type(2.2))
print(type('python'))

Slicing a List

It is possible to use the slicing operator [ ] to extract an item or a range of items from a list. The index starts from 0 in Python.

a = [5,10,15,20,25,30,35,40]
# call an item by its index
a[2]
# call a range of items
a[0:3])
# call all the items from the start to a given index
a[:6]
# call all the items from one index to the end of the list
a[5:]

Comments?

Change a list

Lists are mutable, meaning, the value of elements of a list can be altered.

a = [1, 2, 3]
a[2] = 4
print(a)

Tuple

Tuple is an ordered sequence of items same as a list. The only difference is that tuples are immutable. Tuples once created cannot be modified.

Tuples are used to write-protect data and are usually faster than lists as they cannot change dynamically.

It is defined within parentheses () where items are separated by commas.

It is possible to use the slicing operator [] to extract items but we cannot change its value.

t = (5,'program', 1+3j)
t[1]
t[0:3]

What happen if running this?

t[0] = 10
TypeError: 'tuple' object does not support item assignment

Strings

String is sequence of Unicode characters. You can use single quotes or double quotes to represent strings. Multi-line strings can be denoted using triple quotes, ’’’ or """.

s = "This is a string"
print(s)
s = '''A multiline
string'''
print(s)

Just like a list and tuple, the slicing operator [ ] can be used with strings. Strings, however, are immutable.

s = 'Hello world!'
s[4])
s[6:11]

What happen if running this?

s[5] ='d'
TypeError: 'str' object does not support item assignment

Set

Set is an unordered collection of unique items. Set is defined by values separated by comma inside braces { }. Items in a set are not ordered.

a = {5,2,3,1,4}
print(a)

It is possible to perform set operations like union, intersection on two sets. Sets have unique values. They eliminate duplicates.

a = {1,2,2,3,3,3}
print(a)
b = {2,3,4,5,6,7}
c = a & b
print(c)

Comments?

Since, set are unordered collection, indexing has no meaning. Hence, the slicing operator [] does not work.

a = {1,2,3}
a[1]

Comments?

Traceback (most recent call last):
  File "<string>", line 301, in runcode
  File "<interactive input>", line 1, in <module>
TypeError: 'set' object does not support indexing

Dictionary

Dictionary is an unordered collection of key-value pairs.

It is generally used when we have a huge amount of data. Dictionaries are optimized for retrieving data. We must know the key to retrieve the value.

In Python, dictionaries are defined within braces {} with each item being a pair in the form key:value. Key and value can be of any type.

d = {'value1':56,'value2':77, 'value3': 87}
type(d)
d['value2']
d['value3']
d[77]

Comments?

Traceback (most recent call last):
  File "<string>", line 9, in <module>
KeyError: 77

Data Types Conversion

it is possible to convert between different data types by using different type conversion functions like:

  • int()
  • float()
  • str()
  • etc. etc. etc.

Examples:

# from integer to float
float(5)
# from float to integer
int(-10.6)
# from string to float
float('2.5')
# from integer to string
str(25)

What about this?

int('1p')
Traceback (most recent call last):
  File "<string>", line 301, in runcode
  File "<interactive input>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '1p'

It si possible to convert one sequence to another.

# from list to set
set([1,2,3])
# fromt set to list
tuple({5,6,7})
# from string to list
list('hello')

Or to convert to dictionary, but each element must be a pair.

# from a list of lists to a dictionary
dict([[1,2],[3,4]])
# from a list of tuple to a dictionary
dict([(3,26),(4,44)])

Note: List of Lists

A list of lists is a group of lists put togehter as indipendent elements of another list.

a = [[1,2],[3,4],[5,6],[7,8]]

Try to run

a[2]

and

a[2][0]

Comments?

Input, Output and Import

Ouput

It is possible to use the function

  • print()

to output data to the standard output device as seen before.

print('This sentence is output to the screen')
a = 5
print('The value of a is', a)

In the second example, is possible to notice that a space was added between the string and the value of variable a. This is by default, but it is possible to change it following the syntax of the print() function:

  • print(objects, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)
  • print(objects, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)

The sep separator is used between the values. It defaults into a space character.

After all values are printed, end is printed. It defaults into a new line.

The file is the object where the values are printed and its default value is sys.stdout (screen). Here is an example to illustrate this.

print(1, 2, 3, 4)
print(1, 2, 3, 4, sep='*')
print(1, 2, 3, 4, sep='#', end='&')

Output Formatting Method

Sometimes we would like to format our output to make it look attractive. This can be done by using the method

  • str.format()
x = 5
y = 10
print('The value of x is {} and y is {}'.format(x,y))

Here, the curly braces {} are used as placeholders.

It is possible to specify the order in which they are printed by using numbers:

print('I love {0} and {1}'.format('Bulbasaur','Wailord'))
print('I love {1} and {0}'.format('Bulbasaur','Wailord'))

Note: Method and Function

Until now we talked about functions and methods. We will talk about them later, but for now just keep in mind that they are more or less the same excluding the fact that functions could be called indipendently instead methods must be linked to a specific object using the object.method() syntax

a = 'Ciao!'

# function
print(a, 'master!')

# method
('{} master!'.format(a))

Comments?

Input

It is possible to take the input from the user. In Python there is the function

  • input()

with syntax

  • input([prompt])

Where prompt is the string you wish to display to the user on the screen.

num = input('Enter a number: ')
print(num)
print(type(num))

Comments?

It is possible to see that the entered value 10 is a string, not a number. To convert this into a number you can use int() or float() functions.

num = input('Enter a number: ')
num = int(num)
print(num)
print(type(num))

Import Modules

A module is a Python file (.py) containing Python statements.

Definitions,classes and functions inside a module can be imported inside the interactive interpreter in Python (or into another module) using the import keyword.

Python has pre-installed module called math which contains a variable pi assigned to the pi greco value.

It is possible to import this module

import math

and then call the variable pi

math.pi

Now all the definitions inside math module are available in our enviroment.

print(math.e)
print(math.tau)
print(math.inf)
# and so on...

It is also possible to import a specific definition, function or class using the from keyword.

from math import pi
from math import tau

print(pi)
print(tau)
print(e)

While importing a module, Python looks at several places defined in sys.path. It is a list of directory locations where the module are stored. It is possible to add your own location to this list.

import sys
sys.path

or

from sys import path
path

Operators

Operators

Operators are special symbols in Python that carry out arithmetic or logical computation. The value that the operator operates on is called the operand.

We will see here some of the most used operators with Python.

Arithmetic Operators

Arithmetic operators are used to perform mathematical operations like addition, subtraction, multiplication, etc.

Operator Meaning Example
+ Add two operands x + y+ 2
- Subtract right operand from the left x - y- 2
* Multiply two operands x * y
/ Divide left operand by the right one x / y
** Exponent - left operand raised to the power of right x**y

Comparison Operators

Comparison operators are used to compare values. It returns either True or False according to the condition.

Operator Meaning Example
> Greater than x > y
< Less than x < y
== Equal to x == y
!= Not equal to x != y
>= Greater than or equal to x >= y
<= Less than or equal to x <= y

Logical Operators

Logical operators are the and, or, not operators.

Operator Meaning Example
and True if both the operands are true x and y
or True if either of the operands is true x or y
not True if operand is false not x

Bitwise Operators

Bitwise operators act on operands as if they were strings of binary digits. They operate bit by bit, hence the name.

Assignment Operators

Assignment operators are used in Python to assign values to variables.

Operator Meaning Equivalent to
= x = 5 x = 5
+= x += 5 x = x + 5
-= x -= 5 x = x - 5
*= x *= 5 x = x * 5

Special Operators

Python language offers some special types of operators like the identity operator or the membership operator.

Special Operators: Identity

Operator Meaning Example
is True if the operands are identical (refer to the same object) x is True
is not True if the operands are not identical (do not refer to the same object) x is not True

Special Operators: Membership

Operator Meaning Example
in True if value/variable is found in the sequence 5 in x
not in True if value/variable is not found in the sequence 5 not in x

Flow Control

If.. Else.. Elif.. Statement

Decision making is required when we want to execute a code only if a certain condition is satisfied.

The if elif else statement is used in Python for decision making.

If.. Statement

if test expression:
    statement(s)

Here, the program evaluates the test expression and will execute statement(s) only if the test expression is True.

If the test expression is False, the statement(s) is not executed.

In Python, the body of the if statement is indicated by the indentation. The body starts with an indentation and the first unindented line marks the end.

Python interprets non-zero values as True. None and 0 are interpreted as False.

Example

# If the number is positive, an appropriate message is printed.

num = 3
if num > 0:
    print(num, "is a positive number.")
print("This is always printed.")

num = -1
if num > 0:
    print(num, "is a positive number.")
print("This is also always printed.")

If.. Else.. Statement

if test expression:
    Body of if
else:
    Body of else

The if.. else statement evaluates test expression and will execute the body of if only when the test condition is True.

If the condition is False, the body of else is executed. Indentation is used to separate the blocks.

# Program checks if the number is positive or negative
# And displays an appropriate message

num = 3

# Try these two variations as well. 
# num = -5
# num = 0

if num >= 0:
    print("Positive or Zero")
else:
    print("Negative number")

If.. Elif.. Else.. Statement

if test expression:
    Body of if
elif test expression:
    Body of elif
else: 
    Body of else

The elif is short for else if. It allows you to check for multiple expressions.

If the condition for if is False, it checks the condition of the next elif* block and so on.

If all the conditions are False, the body of else is executed.

Only one block among the several if.. elif.. else blocks is executed according to the condition.

The if block can have only one else block. But it can have multiple elif blocks.

'''In this program, 
we check if the number is positive or
negative or zero and 
display an appropriate message'''

num = 3.4

# Try these two variations as well:
# num = 0
# num = -4.5

if num > 0:
    print("Positive number")
elif num == 0:
    print("Zero")
else:
    print("Negative number")

Nested If Statements

We can have a if.. elif.. else statement inside another if.. elif.. else statement. This is called nesting in computer programming.

Any number of these statements can be nested inside one another. Indentation is the only way to figure out the level of nesting. They can get confusing, so they must be avoided unless necessary.

'''In this program, we input a number
check if the number is positive or
negative or zero and display
an appropriate message
This time we use nested if statement'''

num = float(input("Enter a number: "))
if num >= 0:
    if num == 0:
        print("Zero")
    else:
        print("Positive number")
else:
    print("Negative number")

For Loop

The for loop in Python is used to iterate over a sequence (list, tuple, string) or other iterable objects. Iterating over a sequence is called traversal.

for val in sequence:
    Body of for

Here, val is the variable that takes the value of the item inside the sequence on each iteration.

Loop continues until we reach the last item in the sequence. The body of for loop is separated from the rest of the code using indentation.

# Program to find the sum of all numbers stored in a list

# List of numbers
numbers = [6, 5, 3, 8, 4, 2, 5, 4, 11]

# variable to store the sum
sum = 0

# iterate over the list
for val in numbers:
    sum = sum+val

print("The sum is", sum)

For Loop with Range Function

It is possible to generate a sequence of numbers using the function

  • range()
range(10)

This function will generate numbers from 0 to 9 (10 numbers).

It is also possible define the start, stop and step size with the syntax

range(start, stop, step_size)

The function will generate numbers from start to (stop - 1) using a defined step_size (default is 1).

The range object is “lazy” in a sense because it doesn’t generate every number that it “contains” when it is created. However, it is not an iterator since it supports in and len operations. This function does not store all the values in memory; it would be inefficient. So it remembers the start, stop, step size and generates the next number on the go.

To force this function to output all the items, it is possible to use the function list().

range(10)
list(range(10))
list(range(2, §8))
list(range(2, 20, 3))
# Program to iterate through a list using indexing

genre = ['pop', 'rock', 'jazz']

# iterate over the list using index
for i in range(len(genre)):
    print("I like", genre[i])

For Loop with Else

A for loop can have an optional else block as well. The else part is executed if the items in the sequence used in for loop exhausts.

digits = [0, 1, 5]

for i in digits:
    print(i)
else:
    print("No items left.")

The break keyword can be used to stop a for loop. In such cases, the else part is ignored.

# program to display student's marks from record
student_name = 'Marco'

marks = {'James': 90, 'Jules': 55, 'Arthur': 77}

for student in marks:
    if student == student_name:
        print(marks[student])
        break
else:
    print('No entry with that name found.')

While Loop

The while loop in Python is used to iterate over a block of code as long as the test expression (condition) is true.

It is generally used when you don’t know the number of times to iterate beforehand.

while test_expression:
    Body of while

In the while loop, test expression is checked first. The body of the loop is entered only if the test_expression evaluates to True. After one iteration, the test expression is checked again. This process continues until the test_expression evaluates to False.

In Python, the body of the while loop is determined through indentation.

Python interprets any non-zero value as True. None and 0 are interpreted as False.

# Program to add natural numbers up to sum = 1+2+3+...+n

n = 10

# initialize sum and counter
sum = 0
i = 1

while i <= n:
    sum = sum + i
    i = i+1    # update counter

# print the sum
print("The sum is", sum)

While Loop with Else

Same as with for loops, while loops can also have an optional else block.

The else part is executed if the condition in the while loop evaluates to False.

The while loop can be terminated with a break statement. In such cases, the else part is ignored.

'''Example to illustrate
the use of else statement
with the while loop'''

counter = 0

while counter < 6:
    print("Inside loop")
    counter = counter + 1
    if counter == 5:
        break
else:
    print("Inside else")

Break and Continue

In Python, break and continue statements can alter the flow of a normal loop as we have seen in the previous slide.

Break

The break statement terminates the loop containing it. Control of the program flows to the statement immediately after the body of the loop.

If the break statement is inside a nested loop (loop inside another loop), the break statement will terminate the innermost loop.

# Use of break statement inside the loop

for val in "string":
    if val == "i":
        break
    print(val)

print("The end")
for val1 in "COYOTE":
    print(val1)
    for val2 in range("string"):
        if val1 == "Y":
           break
    print("val2")

print("The End")

Continue

The continue statement is used to skip the rest of the code inside a loop for the current iteration only. Loop does not terminate but continues on with the next iteration.

# Program to show the use of continue statement inside loops

for val in "string":
    if val == "i":
        continue
    print(val)

print("The end")

Functions

Functions

In Python, a function is a group of related statements that performs a specific task.

Functions help break our program into smaller and modular chunks. As our program grows larger and larger, functions make it more organized and manageable.

Furthermore, it avoids repetition and makes the code reusable.

Function syntax:

def function_name(parameters): # parameters are optional
    """docstring""" # optional
    statement(s)
    return value # optional
  1. Keyword def that marks the start of the function header
  2. A function name to uniquely identify the function
  3. Parameters (arguments) through which we pass values to a function. They are optional
  4. A colon (:) to mark the end of the function header
  5. Optional documentation string (docstring)
  6. One or more valid python statements that make up the function body (with identation)
  7. An optional return statement to return a value from the function

Example of a function

Try to run the following chunk

def greet(name):
    """
    This function greets to
    the person passed in as
    a parameter
    """
    print("Hello, " + name + ". Good morning!")

Comments?

greet("Pikachu")

Comments?

Function Docstring

The first string after the function header is called the docstring and is short for documentation string. It is briefly used to explain what a function does.

Although optional, documentation is a good programming practice. Unless you can remember what you had for dinner last week, always document your code.

This string is available to us as the attribute of the function

__doc__

print(greet.__doc__)

Function Return Statement

The return statement is used to exit a function and go back to the place from where it was called using the syntax

return [expression_list]

This statement can contain an expression that gets evaluated and the value is returned. If there is no expression in the statement or the return statement itself is not present inside a function, then the function will return the None object.

Try

greet("Squirtle")

And

print(greet("squirtle"))

Comments?

In the second example, None is the returned value since greet() function directly prints the name and no return statement is used.

Try to make a greet2() function identical to the greet() function but with a return statement that return the input name.

def greet2(name):
    """
    This function greets to
    the person passed in as
    a parameter and returns it
    """
    print("Hello, " + name + ". Good morning!")
    return name

Try it

print(greet("Pikachu"))
print(greet2("Pikachu"))

or

x = greet("Pikachu")
y = greet("Pikachu")

print(x)
print(y)

Comments?

Note: Lifetime of Variables Inside Functions

Parameters and variables defined inside a function are not visible from outside the function.

The lifetime of a variable is the period throughout which the variable exits in the memory. The lifetime of variables inside a function is as long as the function executes.

They are destroyed once we return from the function. Hence, a function does not remember the value of a variable from its previous calls.

We will talk about this later in the chapter of global and local variables.

Function Arguments

In Python, it is possible to define a function that takes variable number of arguments.

def greet(name, msg):
    """This function greets to
    the person with the provided message"""
    print("Hello", name + ', ' + msg)
greet("Monica", "Good morning!")

Since we have called this function with two arguments, it runs smoothly and we do not get any error.

greet("Monica")

If we call it with a different number of arguments, the interpreter will show an error message.

TypeError: greet() missing 1 required positional argument: 'msg'

Variable Function Arguments

Up until now, functions had a fixed number of arguments. In Python, there are other ways to define a function that can take variable number of arguments.

  1. Default Arguments
  2. Keyword Arguments
  3. Arbitrary Arguments

Default Arguments

Function arguments can have default values in Python.

We can provide a default value to an argument by using the assignment operator (=).

def greet(name, msg="Good morning!"):
    """
    This function greets to
    the person with the
    provided message.

    If the message is not provided,
    it defaults to "Good
    morning!"
    """

    print("Hello", name + ', ' + msg)

Try

greet("Kate")

And

greet("Bruce", "How do you do?")

In this function, the parameter name does not have a default value and is required (mandatory) during a call.

On the other hand, the parameter msg has a default value of “Good morning!”. So, it is optional during a call. If a value is provided, it will overwrite the default value.

Any number of arguments in a function can have a default value. But once you have a default argument, all the arguments to its right must also have default values.

def greet(msg = "Good morning!", name):
SyntaxError: non-default argument follows default argument

Keyword Argument

When we call a function with some values, these values get assigned to the arguments according to their position.

greet("Bruce", "How do you do?")

For example, in the above function greet(), when we called it as greet(“Bruce”, “How do you do?”), the value "Bruce" gets assigned to the argument name and similarly “How do you do?” to msg.

Python allows functions to be called using keyword arguments. When we call functions in this way, the order (position) of the arguments can be changed.

greet(name = "Bruce",msg = "How do you do?")

Or

greet(msg = "How do you do?",name = "Bruce") 

are the same indipentently by the fact that some arguments could be mandatory or default.

It is possible to mix positional arguments with keyword arguments during a function call. But you have tot keep in mind that keyword arguments must follow positional arguments.

Right

greet("Bruce", msg = "How do you do?") 

Wrong

greet(name="Bruce","How do you do?")
SyntaxError: non-keyword arg after keyword arg

Arbitrary Arguments

Sometimes, we do not know in advance the number of arguments that will be passed into a function. Python allows us to handle this kind of situation through function calls with an arbitrary number of arguments.

In the function definition, it is used an asterisk (*) before the parameter name to denote this kind of argument.

def greet(*names):
    """This function greets all
    the person in the names tuple."""

    # names is a tuple with arguments
    for name in names:
        print("Hello", name)

And call this function using as arguments more than one string

greet("Lara", "Zelda", "Peach", "Samus", "Jill")

Recursive Function

In Python is possible for a function to call itself. These types of construct are termed as recursive functions.

Factorial of a number is the product of all the integers from 1 to that number. > 1*2*3*4*5*6 = 720

This function generate the factorial of a given number

def factorial(x):
    """This is a recursive function
    to find the factorial of an integer"""

    if x == 1:
        return 1
    else:
        return (x * factorial(x-1))
num = 3
print("The factorial of", num, "is", factorial(num))

In the above example, factorial() is a recursive function as it calls itself.

def factorial(x):
    """This is a recursive function
    to find the factorial of an integer"""

    if x == 1:
        return 1
    else:
        return (x * factorial(x-1))
factorial(3)          # 1st call with 3
3 * factorial(2)      # 2nd call with 2
3 * 2 * factorial(1)  # 3rd call with 1
3 * 2 * 1             # return from 3rd call as number=1
3 * 2                 # return from 2nd call
6                     # return from 1st call

This recursion ends when the number reduces to 1. This is called the base condition.

Every recursive function must have a base condition that stops the recursion or else the function calls itself infinitely.

The Python interpreter limits the depths of recursion to help avoid infinite recursions, resulting in stack overflows.

By default, the maximum depth of recursion is 1000. If the limit is crossed, it results in RecursionError.

Try

def recursor():
    recursor()
recursor()
RecursionError: maximum recursion depth exceeded

Anonymous or Lambda Function

In Python, an anonymous function is a function that is defined without a name.

While normal functions are defined using the def keyword in Python, anonymous functions are defined using the lambda keyword with the following syntax

lambda arguments: expression

Lambda functions can have any number of arguments but only one expression. The expression is evaluated and returned.

double = lambda x: x * 2

print(double(5))

This function has no name. It returns a function object which is assigned to the identifier double.

We use lambda functions when we require a nameless function for a short period of time.

In Python, we generally use it as an argument to a higher-order function (a function that takes in other functions as arguments).

Gobal and Local

Global

In Python, a variable declared outside of a function is known as a global scope variable. This means that a global variable can be accessed inside or outside a function.

x = "global"

def printer():
    print(x)
printer()
print(x)

Comments?

What if you want to change the value of x inside a function?

x = "global"

def doubler():
    x = x * 2
    print(x)
doubler()
UnboundLocalError: local variable 'x' referenced before assignment

The output shows an error because Python treats x as a local variable and x is not defined inside doubler() (the local scope).

Global Keyword

How to change a global variable in a local scope?

It is possible to use the keyword

global

x = "global"

def doubler():
    global x
    x = x * 2
    print(x)
doubler()
print(x)

Comments?

In Python, global keyword allows you to modify the variable outside of the current scope. It is used to create a global variable and make changes to the variable in a local context.

Local Variables

A variable declared inside the function’s body is known as a local variable.

def printer():
    y = "local"
    print(y)
printer()
print(y)
NameError: name 'y' is not defined

Comments?

The output shows an error because you are trying to access a local variable y in a global scope whereas the local variable only works inside printer() (the local scope).

Global and Local Together

x = 5

def printer():
    x = 10
    print(x)
printer()
print(x)

Comments?

In the above code, it is used the same name x for both global variable and local variable. You get a different result when you print the same variable because the variable is declared in both scopes, the local scope inside printer() and global scope outside printer().

Nonlocal Keyword

Nonlocal keyword creates nonlocal variables that are used in nested functions whose local scope is not defined. This means that the variable can be neither in the local nor the global scope.

x = "global"

def outer():
    x = "local"

    def inner():
        nonlocal x
        x = "nonlocal"
        print("inner:", x)

    inner()
    print("outer:", x)
outer()
print(x)

Remember that if you change the value of a nonlocal variable, the changes appear in the local variable.

Python Object Oriented Programming

Python Object Oriented Programming

Python is a multi-paradigm programming language. It supports different programming approaches.

One of the popular approaches to solve a programming problem is by creating objects. This is known as Object-Oriented Programming (OOP).

An object has two characteristics:

  1. Data
  2. Behaviours

A parrot could be an object, as it has the following properties:

  1. Data: “Captain Flint”, 20, “red”
  2. Behaviours: sings, dances, flyes

A class is a blueprint for the object. It specifies which

  1. attributes (data categories)

and which

  1. methods (type of behaviours)

actually define a Parrot.

A class for Parrot could be:

  1. Attributes: name, age, color
  2. Methods: singing, dancing, flying

Class

A class is a blueprint for the object.

It is possible to use the keyword

class

to define an empty class.

Try to define an empty class for Parrot.

class Parrot():
    pass 

Object

From class, we construct instances. An instance is a specific object created from a particular class.

When class is defined, only the description for the object is defined. Therefore, no memory or storage is allocated.

obj = Parrot()

Here, obj is an object of class Parrot and actually occupies storage inside memory.

Creating Class and Object: Attributes

class Parrot:
    # instance attribute
    def __init__(self, name, age):
        self.name = name
        self.age = age

In the above program, it is created a class with the name Parrot. Then, the Parrot attributes are defined.

These attributes are defined inside the init method of the class. It is the initializer method that is first run as soon as the object is created.

AND YES, you could guess now that also Classes are objects in Python because they have their own methods to use (remind that methods are functions associated to object)

Then, it is possible to create instances of the Parrot class.

# instantiate the Parrot class
blu = Parrot("Blu", 10)
woo = Parrot("Woo", 15)

Here, blu and woo are references (value) to our new objects.

You can access the instance attributes using:

print("{} is {} years old".format( blu.name, blu.age))
print("{} is {} years old".format( woo.name, woo.age))

The instances attribute (attribute of object) are specific of each object you create.

You can also define class attributes that are attributes identical to all objects generated from that class.

class Parrot:

    # class attribute
    species = "bird"

    # instance attribute
    def __init__(self, name, age):
        self.name = name
        self.age = age

Generate again the objects and access the class attribute

blu = Parrot("Blu", 10)
woo = Parrot("Woo", 15)
# access the class attributes
print("Blu is a {}".format(blu.__class__.species))
print("Woo is also a {}".format(woo.__class__.species))

Creating Class and Object: Methods

Methods are functions defined inside the body of a class. They are used to define the behaviors of an object.

class Parrot:
    
    # instance attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    # instance method
    def sing(self, song):
        return "{} sings {}".format(self.name, song)

    def dance(self):
        return "{} is now dancing".format(self.name)

In the above program, are defined two methods i.e sing() and dance(). These are called instance methods because they are called on an instance object i.e blu.

Now you can generate new objects and call methods on them.

# instantiate the object
blu = Parrot("Blu", 10)
# call our instance methods
print(blu.sing("'Happy'"))
print(blu.dance())

Inheritance

Inheritance is a way of creating a new class for using details of an existing class without modifying it. The newly formed class is a derived class (or child class). Similarly, the existing class is a base class (or parent class).

Penguins are birds so you would think to create a parent bird class with a penguin child class

In the program below, two classes are created: Bird, parent class and Penguin, child class.

# parent class
class Bird:
    def __init__(self,name):
        self.name = name
    def fly(self):
        print("{} flyes in the sky.".format(self.name))
    def swim(self):
        print("{} swims in the sea.".format(self.name))
        
# child class
class Penguin(Bird):
    def __init__(self,name):
        self.name = name
    def fly(self):
        print("{} cannot fly!".format(self.name))
    def freezeResistence(self):
        print("{} resists to freezing conditions".format(self.name))

Then it is possible to generate a bird and a penguin

bird_1 = Bird("Titti")
penguin_1 = Penguin('HappyFeet')

that have their owns attributes

print(bird_1.name) 
print(penguin_1.name)

Now try the following by looking the 2 classes definition

bird_1.swim()
penguin_1.swim()

Comments?

The child class inherits the method of parent class.

Now try the following by looking the 2 classes definition

bird_1.fly()
penguin_1.fly()

Comments?

The child class modified the behavior of the parent class.

Now try the following by looking the 2 classes definition

penguin_1.freezeResistance()
bird_1.freezeResistance()

Comments?

Child class extended the methods of the parent class without modifying the parent class.

Encapsulation

Using OOP in Python, it is possible to restrict access to methods and variables. This prevents data from direct modification which is called encapsulation. In Python, we denote private attributes using underscore as the prefix.

Create a class with a private attribute and then try to modify it

class Computer:
    def __init__(self,name):
        self.name = name
        self.__maxprice = 900
    def sell(self):
        print("Selling Price: {}".format(self.__maxprice))

Create the object

c = Computer("Corsair")

Check the properties

print(c.name)
c.sell()

Try to modify them

c.name = "Asus"
c.__maxprice = "1000"

print(c.name)
c.sell()

Comments?

Polymorphism

Polymorphism is an ability (in OOP) to use a common interface for multiple forms.

In other words it is possible to create a function able to call a specific method of any object.

Create the classes and the common interface

# casses
class Parrot:
    def fly(self):
        print("Parrot can fly")

class Penguin:
    def fly(self):
        print("Penguin can't fly")

# common interface
def flying_test(bird):
    bird.fly()

Create the objects

captainFlint = Parrot()
happyFeet = Penguin()

Using the common interface to call the fly() method on both objects

flying_test(blu)
flying_test(peggy)

Questions?

// reveal.js plugins